package com.piece.spider.controller;

import com.piece.core.framework.constant.ExceptionConstants;
import com.piece.core.framework.annotation.permission.AclPermission;
import com.piece.core.framework.annotation.permission.SubmitPermission;
import com.piece.core.framework.support.bootstrap.DataTable;
import com.piece.core.framework.support.bootstrap.QueryTable;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.support.response.AjaxResponse;
import com.piece.core.framework.util.basic.I18nUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.framework.util.file.FileUtil;
import com.piece.core.web.controller.FrameWorkController;
import com.piece.module.spider.annotation.Comment;
import com.piece.module.spider.executor.Grammerable;
import com.piece.module.spider.executor.function.FunctionExecutor;
import com.piece.module.spider.executor.function.extension.FunctionExtension;
import com.piece.module.spider.model.SpiderFlow;
import com.piece.module.spider.service.SpiderFlowService;
import com.piece.module.spider.support.Grammer;
import com.piece.module.spider.util.ExecutorsUtil;
import com.piece.module.spider.util.io.Line;
import com.piece.module.spider.util.io.RandomAccessFileReader;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@RefreshScope
@RestController
@Api(tags = {"爬虫任务管理"})
@RequestMapping(value = "/api/spider/flow")
public class SpiderFlowController extends FrameWorkController {

    @Value("${spider.workspace:#{null}}")
    private String workspace;

    private final List<Grammer> grammers = new ArrayList<>();

    @Resource
    private SpiderFlowService spiderFlowService;

    @Autowired
    private List<FunctionExecutor> functionExecutors;

    @Autowired
    private List<FunctionExtension> functionExtensions;

    @Autowired
    private List<Grammerable> grammerables;

    @PostConstruct
    private void init(){
        for (FunctionExecutor executor : functionExecutors) {
            String function = executor.getFunctionPrefix();
            grammers.addAll(Grammer.findGrammers(executor.getClass(),function,function,true));
            Comment comment = executor.getClass().getAnnotation(Comment.class);
            Grammer grammer = new Grammer();
            if(null!= comment){
                grammer.setComment(comment.value());
            }
            grammer.setFunction(function);
            grammers.add(grammer);
        }

        for (FunctionExtension extension : functionExtensions) {
            String owner = extension.support().getSimpleName();
            grammers.addAll(Grammer.findGrammers(extension.getClass(),null,owner,true));
        }
        for (Grammerable grammerable : grammerables) {
            grammers.addAll(grammerable.grammers());
        }

        try {
            spiderFlowService.initJobs();
        } catch (Exception e) {
            log.error("自动执行爬虫任务异常：{}", e.getMessage());
        }
    }

    @RequestMapping(value = "/table")
    @AclPermission(code = "spider:program:view")
    @ApiOperation(value = "获取爬虫定义分页数据")
    @ApiImplicitParam(value = "查询条件", dataTypeClass = QueryTable.class, type = "body")
    public AjaxResponse table() {
        QueryTable queryTable = initTable();
        DataTable<SpiderFlow> dataTable = spiderFlowService.findPage(queryTable);
        return success(dataTable);
    }

    @RequestMapping("/load")
    @AclPermission(code = "spider:program:view")
    @ApiOperation(value = "获取爬虫定义")
    @ApiImplicitParam(name = "id", value = "爬虫定义主键")
    public AjaxResponse load() {
        String id = getRequest().getParameter("id");
        return success(spiderFlowService.findById(id));
    }

    @SubmitPermission
    @PostMapping(value = "/save")
    @AclPermission(code = "spider:program:add,spider:program:edit")
    @ApiOperation(value = "保存爬虫定义")
    @ApiImplicitParam(value = "爬虫定义", dataTypeClass = SpiderFlow.class, type = "body")
    public AjaxResponse save() {
        SpiderFlow spiderFlow = new SpiderFlow();
        try {
            initDB(spiderFlow);
            spiderFlow = spiderFlowService.insert(spiderFlow);
            return success(I18nUtil.message(ExceptionConstants.SAVE_SUCCESS), spiderFlow.getId());
        } catch (Exception e) {
            return error(I18nUtil.message(ExceptionConstants.SAVE_FAILURE), e.getMessage());
        }
    }

    @SubmitPermission
    @PostMapping("/delete/{id}")
    @AclPermission(code = "spider:program:delete")
    @ApiOperation(value = "删除爬虫定义")
    @ApiImplicitParam(name = "id", value = "爬虫定义主键", type = "path")
    public AjaxResponse delete(@PathVariable long id) {
        try {
            spiderFlowService.delete(id);
            return success(I18nUtil.message(ExceptionConstants.DELETE_SUCCESS));
        } catch (Exception e) {
            return error(I18nUtil.message(ExceptionConstants.DELETE_FAILURE), e.getMessage());
        }
    }

    @RequestMapping(value = "/other/{id}")
    @AclPermission(code = "spider:program:view")
    @ApiOperation(value = "获取其它爬虫定义列表")
    @ApiImplicitParam(name = "id", value = "爬虫定义ID", type = "path")
    public AjaxResponse other(@PathVariable String id) {
        return success(spiderFlowService.selectFlows(id));
    }

    @RequestMapping(value = "/history")
    @AclPermission(code = "spider:program:view")
    @ApiOperation(value = "获取历史记录")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "记录ID"),
            @ApiImplicitParam(name = "timestamp", value = "时间戳")
    })
    public AjaxResponse history() {
        try {
            String id = getRequest().getParameter("id");
            String timestamp = getRequest().getParameter("timestamp");
            if (StringUtil.isNotEmpty(timestamp)) {
                return success(spiderFlowService.readHistory(Convert.toLong(id), timestamp));
            } else {
                return success(spiderFlowService.historyList(Convert.toLong(id)));
            }
        } catch (Exception e) {
            return error(I18nUtil.message(ExceptionConstants.MESSAGE_ERROR), e.getMessage());
        }
    }

    @RequestMapping(value = "/log")
    @AclPermission(code = "spider:program:view")
    @ApiOperation(value = "获取日志记录")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "日志ID"),
            @ApiImplicitParam(name = "taskId", value = "任务ID"),
            @ApiImplicitParam(name = "keywords", value = "搜索词"),
            @ApiImplicitParam(name = "index", value = "起始位置", dataType = "long"),
            @ApiImplicitParam(name = "count", value = "读取数量", dataType = "int"),
            @ApiImplicitParam(name = "reversed", value = "倒叙", dataType = "boolean"),
            @ApiImplicitParam(name = "matchcase", value = "区分大小写", dataType = "boolean"),
            @ApiImplicitParam(name = "regx", value = "搜索表达式")
    })
    public AjaxResponse log() {
        long id = Convert.toLong(getRequest().getParameter("id"));
        String taskId = getRequest().getParameter("taskId");
        String keywords = getRequest().getParameter("keywords");
        long index = Convert.toLong(getRequest().getParameter("index"), -1L);
        int count = Convert.toInt(getRequest().getParameter("count"), 10);
        boolean reversed = Convert.toBool(getRequest().getParameter("reversed"), true);
        boolean matchcase = Convert.toBool(getRequest().getParameter("matchcase"), false);
        boolean regx = Convert.toBool(getRequest().getParameter("regx"), false);
        if (StringUtil.isBlank(taskId)) {
            String maxId = spiderFlowService.getFlowMaxLogId(id);
            taskId = null == maxId ? "" : maxId.toString();
        }
        SpiderFlow spiderFlow = spiderFlowService.findById(id);
        String path = spiderFlow.getXmlFilePath();
        path = path.substring(0, path.indexOf(workspace));
        File logFile = new File(path + workspace, id + File.separator + "logs" + File.separator + taskId + ".log");
        try (RandomAccessFileReader reader = new RandomAccessFileReader(new RandomAccessFile(logFile,"r"), index, reversed)) {
            List<Line> lines = reader.readLine(count, keywords, matchcase, regx);
            return success(lines);
        } catch(FileNotFoundException e){
            return error("日志文件不存在");
        } catch (IOException e) {
            return error("读取日志文件出错");
        }
    }

    @SubmitPermission
    @RequestMapping(value = "/log/download")
    @ApiOperation(value = "下载日志记录")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "日志ID"),
            @ApiImplicitParam(name = "taskId", value = "任务ID")
    })
    public void fileDownload(HttpServletRequest request, HttpServletResponse response) {
        try {
            String id = request.getParameter("id");
            String taskId = request.getParameter("taskId");
            SpiderFlow spiderFlow = spiderFlowService.findById(Convert.toLong(id));
            if (null != spiderFlow && StringUtil.isNotEmpty(taskId)) {
                String path = spiderFlow.getXmlFilePath();
                path = path.substring(0, path.indexOf(workspace));
                File file = new File(path + workspace, id + File.separator + "logs" + File.separator + taskId + ".log");
                String pathName = spiderFlow.getName() + "_" + taskId + ".log";
                FileUtil.downloadForWeb(request, response, file, pathName);
            }
        } catch (Exception e) {
            log.error("下载文件失败", e);
        }
    }

    @SubmitPermission
    @RequestMapping(value = "/cron")
    @AclPermission(code = "spider:program:edit")
    @ApiOperation(value = "重置爬虫定时器")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "任务ID"),
            @ApiImplicitParam(name = "cron", value = "表达式")
    })
    public AjaxResponse cron() {
        try {
            String id = getRequest().getParameter("id");
            String cron = getRequest().getParameter("cron");
            spiderFlowService.resetCornExpression(Convert.toLong(id), cron);
            return success(I18nUtil.message(ExceptionConstants.MESSAGE_SUCCESS));
        } catch (Exception e) {
            return error(I18nUtil.message(ExceptionConstants.MESSAGE_ERROR), e.getMessage());
        }
    }

    @RequestMapping(value = "/xml/{id}")
    @AclPermission(code = "spider:program:view")
    @ApiOperation(value = "获取爬虫xml内容")
    @ApiImplicitParam(name = "id", value = "任务ID", type = "path")
    public AjaxResponse xml(@PathVariable Long id) {
        try {
            return success(spiderFlowService.findById(id).getXml());
        } catch (Exception e) {
            return error(I18nUtil.message(ExceptionConstants.MESSAGE_ERROR), e.getMessage());
        }
    }

    @RequestMapping(value = "/grammers")
    @AclPermission(code = "spider:program:view")
    @ApiOperation(value = "获取所有执行器方法名")
    public AjaxResponse grammers() {
        return success(this.grammers);
    }

    @PostMapping(value = "/shapes")
    @AclPermission(code = "spider:program:view")
    @ApiOperation(value = "获取执行器")
    public AjaxResponse shapes() {
        try {
            return success(ExecutorsUtil.shapes());
        } catch (Exception e) {
            return error(I18nUtil.message(ExceptionConstants.MESSAGE_ERROR), e.getMessage());
        }
    }

    @SubmitPermission
    @RequestMapping(value = "/start/{id}")
    @AclPermission(code = "spider:program:edit")
    @ApiOperation(value = "启动爬取流程")
    @ApiImplicitParam(name = "id", value = "任务ID", type = "path")
    public AjaxResponse start(@PathVariable Long id) {
        try {
            spiderFlowService.start(id);
            return success(I18nUtil.message("spider.startSuccess"));
        } catch (Exception e) {
            return error(I18nUtil.message("spider.startFailure"), e.getMessage());
        }
    }

    @SubmitPermission
    @RequestMapping(value = "/stop/{id}")
    @AclPermission(code = "spider:program:edit")
    @ApiOperation(value = "停止爬取流程")
    @ApiImplicitParam(name = "id", value = "任务ID", type = "path")
    public AjaxResponse stop(@PathVariable Long id) {
        try {
            spiderFlowService.stop(id);
            return success(I18nUtil.message("spider.stopSuccess"));
        } catch (Exception e) {
            return error(I18nUtil.message("spider.stopFailure"), e.getMessage());
        }
    }

    @SubmitPermission
    @RequestMapping(value = "/run/{id}")
    @AclPermission(code = "spider:program:edit")
    @ApiOperation(value = "执行一次爬取流程")
    @ApiImplicitParam(name = "id", value = "任务ID", type = "path")
    public AjaxResponse run(@PathVariable Long id) {
        try {
            spiderFlowService.run(id);
            return success(I18nUtil.message(ExceptionConstants.MESSAGE_SUCCESS));
        } catch (Exception e) {
            return error(I18nUtil.message(ExceptionConstants.MESSAGE_ERROR), e.getMessage());
        }
    }

    @RequestMapping(value = "/recent5TriggerTime")
    @AclPermission(code = "spider:program:view")
    @ApiOperation(value = "获取最近运行时间")
    @ApiImplicitParam(name = "cron", value = "表达式")
    public AjaxResponse recent5TriggerTime(){
        String cron = getRequest().getParameter("cron");
        try {
            return success(spiderFlowService.getRecentTriggerTime(cron, 5));
        } catch (Exception e) {
            return error(I18nUtil.message(ExceptionConstants.MESSAGE_ERROR), e.getMessage());
        }
    }
}
